package com.wiscamp.ninechapters.problems.domain.services;

import com.wiscamp.ninechapters.common.GlobalDictionary;
import com.wiscamp.ninechapters.common.GlobalState;
import com.wiscamp.ninechapters.problems.domain.events.AssignmentCreatedEvent;
import com.wiscamp.ninechapters.problems.domain.events.AssignmentUpdatedEvent;
import com.wiscamp.ninechapters.problems.domain.gateway.DictionaryGateway;
import com.wiscamp.ninechapters.problems.domain.models.tasks.Assignment;
import com.wiscamp.ninechapters.problems.domain.models.tasks.AssignmentDetail;
import com.wiscamp.ninechapters.problems.domain.repositories.AssignmentRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;

@Service
public class AssignmentService extends BaseDomainService {

    private final AssignmentRepository assignmentRepository;

    private final DictionaryGateway dictionaryGateway;

    private final String CACHE_NAME_ASSIGNMENT = "assignment";

    public AssignmentService(AssignmentRepository assignmentRepository, DictionaryGateway dictionaryGateway) {
        this.assignmentRepository = assignmentRepository;
        this.dictionaryGateway = dictionaryGateway;
    }

    /**
     * Gets a assignment - 练习任务指派记录
     *
     * @param assignmentId
     * @return
     */
    @Cacheable(value = CACHE_NAME_ASSIGNMENT, key = "#assignmentId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Assignment getAssignment(@Min(0) long assignmentId) {
        return assignmentRepository.getAssignment(assignmentId);
    }

    @Transactional(transactionManager = "problemsTransactionManager")
    @HandleServiceException
    @CachePut(value = CACHE_NAME_ASSIGNMENT, key = "#assignment.assignmentId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public Assignment createAssignment(@NotNull Assignment assignment, @Min(0) long operatorId) {
        // save assignment, assign to students by default
        int assignmentTypeId = dictionaryGateway.getDictionaryIdByCode(GlobalDictionary.DISTRIBUTIONTYPE_STUDENT,
                GlobalState.APPLICATION_ID);
        assignment.setAssignmentTypeId(assignmentTypeId);
        assignment.setAssignmentTime(LocalDateTime.now());
        var newAssignment = assignmentRepository.saveAssignment(assignment);
        // create assignment task for each student
        applicationContext.publishEvent(new AssignmentCreatedEvent(this, newAssignment.getAssignmentId()));
        return newAssignment;
    }

    /**
     * Updates a assignment
     *
     * @param assignment
     * @return
     */
    @HandleServiceException
    @CachePut(value = CACHE_NAME_ASSIGNMENT, key = "#assignment.assignmentId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public Assignment updateAssignment(@NotNull Assignment assignment, @Min(0) long operatorId) {
        var updatedAssignment = assignmentRepository.saveAssignment(assignment);
        applicationContext.publishEvent(new AssignmentUpdatedEvent(this, updatedAssignment.getAssignmentId()));
        return updatedAssignment;
    }

    /**
     * Deletes a assignment
     *
     * @param assignmentId
     * @return
     */
    @HandleServiceException
    @CacheEvict(value = CACHE_NAME_ASSIGNMENT, key = "#assignmentId")
    public boolean deleteAssignment(@Min(0) long assignmentId, @Min(0) long operatorId) {
        assignmentRepository.deleteAssignment(assignmentId);
        return true;
    }

    /**
     * Creates a assignment by a set for a user
     *
     * @param setId
     * @param userId
     * @return
     */
    @HandleServiceException
    public Assignment createAssignmentBySet(@Min(0) long setId, @Min(0) long userId, @Min(0) long operatorId) {
        var assignment = new Assignment();
        assignment.setAssignmentUserId(operatorId);
        assignment.setSetId(setId);

        Collection<AssignmentDetail> details = new ArrayList<>();
        var detail = new AssignmentDetail();
        detail.setReferenceId(userId);
        details.add(detail);
        assignment.setDetails(details);
        return createAssignment(assignment, operatorId);
    }
}
